import { ProjectService } from "@/service/projects-api"
import { UserScriptWorker } from "@/us/worker"
import { ComUtils } from "./Com"
import { checkAndUpdateScripts } from "./update-script"

export class ChromeWorker {
    static settingTabID = 0
    userWorker: UserScriptWorker
    private lastWindowId = 0
    constructor() {
        this.initEvents()
        this.userWorker = UserScriptWorker.init(this)
    }
    static init() {
        const worker = new ChromeWorker()
        return worker
    }
    private async initEvents() {
        // dev
        try {
            chrome.userScripts.configureWorld({
                messaging: true
            });
        } catch (error) {
            console.log(error)
        }

        chrome.runtime.onMessage.addListener((message: ChromeMessage, sender, sendResponse) => {
            if (sender.tab) {
                this.lastWindowId = sender.tab.windowId
            }
            if (message.type === 'openSidePanel' && this.lastWindowId) {
                chrome.sidePanel.open({
                    windowId: this.lastWindowId
                })
                chrome.runtime.sendMessage({ type: 'side-panel-reload-script' })
                    .catch(() => { })
                sendResponse(ChromeWorker.getChromeResponse(true, undefined))
                return true
            }
            const func: any = Reflect.get(this, `_${message.type}`)
            if (func && typeof func === 'function') {
                func.call(this, message, sender.tab).then(sendResponse)
            } else {
                const userFunc: any = Reflect.get(this.userWorker, `_${message.type}`)
                if (userFunc && typeof userFunc === 'function') {
                    userFunc.call(this.userWorker, message, sender.tab).then(sendResponse)
                } else {
                    console.log(`unknown message type: ${message.type}`)
                    sendResponse(ChromeWorker.getChromeResponse(false, undefined, `unknown message type: ${message.type}`))
                }
            }

            return true
        })

        //安装时
        chrome.runtime.onInstalled.addListener(() => {
            this.initContextMenus();
        });

        //右键菜单
        chrome.contextMenus.onClicked.addListener((info) => {
            if (info.menuItemId === 'scriptBox') {
                this._openSettings();
            }
        });

        //标签管理
        chrome.tabs.onRemoved.addListener(tabId => {
            if (tabId === ChromeWorker.settingTabID) {
                ChromeWorker.settingTabID = 0
            }
        })

        //自动更新脚本
        chrome.runtime.onStartup.addListener(() => {
            checkAndUpdateScripts()
        })

    }

    public initContextMenus() {
        chrome.contextMenus.removeAll();
        chrome.contextMenus.create({
            id: 'scriptBox',
            title: chrome.i18n.getMessage('app_name'),
            contexts: ['all']
        });

    }

    async _initPopup() {
        const curWin = await chrome.windows.getCurrent()
        if (curWin.id) {
            this.lastWindowId = curWin.id
        }
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _checkAndUpdateScripts() {
        await checkAndUpdateScripts()
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _openSettings(query?: string) {
        chrome.tabs.query({}, async function (tabs) {
            const targetUrl = chrome.runtime.getURL('/settings/index.html' + (typeof query === 'string' ? query : ''))
            let stTab: chrome.tabs.Tab | undefined
            for (const tab of tabs) {
                if (!tab.id) {
                    continue
                }
                if (tab.id === ChromeWorker.settingTabID) {
                    stTab = tab
                    break
                }

            }
            if (!stTab) {
                chrome.tabs.create({
                    url: targetUrl
                }).then(tab => {
                    ChromeWorker.settingTabID = tab.id || 0
                });
            } else {
                stTab.active = true
                chrome.tabs.update(stTab.id || ChromeWorker.settingTabID, {
                    active: true,
                    url: targetUrl
                })
            }

        });
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _setSettingTab(_msg: ChromeMessage, tab?: chrome.tabs.Tab) {
        if (ChromeWorker.settingTabID === 0) {
            ChromeWorker.settingTabID = tab?.id || 0
        }
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _showExtPopup() {
        try {
            await chrome.action.openPopup()
            return ChromeWorker.getChromeResponse(true, undefined)
        } catch (error) {
            return ChromeWorker.getChromeResponse(false, undefined, error)
        }
    }

    async _createWindow(url: string, width: number, height: number) {
        const win = await chrome.windows.create({
            url,
            type: 'popup',
            height,
            width
        })
        return win
    }

    async _openTab(msg: ChromeMessage) {
        try {
            if (!msg.data.url) {
                return ChromeWorker.getChromeResponse(false, undefined, 'open tab error,url not null')
            }
            if (msg.data.new === false) {

                chrome.tabs.update({
                    url: msg.data.url
                })
                return ChromeWorker.getChromeResponse(true, undefined)
            }

            await chrome.tabs.create({
                url: msg.data.url
            })
            return ChromeWorker.getChromeResponse(true, undefined)
        } catch (error) {
            console.error(error)
            return ChromeWorker.getChromeResponse(false, undefined, 'open tab error')
        }
    }

    static getChromeResponse(state: boolean, data?: any, message?: any) {
        const res: ChromeResponse = {
            state,
            data,
            message
        }

        return res
    }

    async _updateScripts() {
        await this.userWorker.registOrUpdateScripts();
        return ChromeWorker.getChromeResponse(true, undefined);
    }

    async _updateScript(message: ChromeMessage) {
        const { id } = message.data;
        const res = await this.userWorker.updateScript(id);
        if (res) {
            return res;
        }
        return ChromeWorker.getChromeResponse(false, undefined);
    }

    async _version() {
        return ChromeWorker.getChromeResponse(true, chrome.runtime.getManifest().version)
    }

    async _isDev() {
        try {
            const dev = chrome.userScripts !== undefined
            return ChromeWorker.getChromeResponse(true, dev)
        } catch (error) {
            return ChromeWorker.getChromeResponse(true, false)
        }
    }

    async _runScript(message: ChromeMessage) {
        const { id, sw, sh } = message.data
        const scripts = await ProjectService.list()
        const script = scripts.find(s => s.id === id)
        if (!script) {
            return ChromeWorker.getChromeResponse(false, undefined, 'script not found')
        }
        if (script.type === 'sidePanel' && typeof chrome.sidePanel !== 'undefined') {
            try {
                await chrome.sidePanel.setOptions({
                    path: chrome.runtime.getURL(`/scripts.html?id=${script.id}`),
                    enabled: true
                })
                return ChromeWorker.getChromeResponse(true, this.lastWindowId)
            } catch (error) {
                const defaultIcon = ComUtils.getResourceUrl('/logo.png')
                chrome.notifications.create({
                    message: ComUtils.tr('side_panel_run_error'),
                    iconUrl: defaultIcon,
                    title: ComUtils.tr('system_error'),
                    type: 'basic'
                })
                console.error('not support sidePanel', error)
            }

        }

        try {

            const info = script.window
            const width = Math.round(Number(info?.width || 300))
            const height = Math.round(Number(info?.height || 450))
            const x = Math.round(sw / 2 - width / 2)
            const y = Math.round(sh / 2 - height / 2)
            const win = await chrome.windows.create({
                url: chrome.runtime.getURL(`/scripts.html?id=${script.id}`),
                left: Number(info?.left || 0) || x,
                top: Number(info?.top || 0) || y,
                width,
                height,
                type: 'popup',
            })

            return ChromeWorker.getChromeResponse(true, win.id)
        } catch (error) {
            console.log(error)
            return ChromeWorker.getChromeResponse(false, undefined, 'run script error')
        }
    }

    /**
     * 独立脚本
     * @param message 
     * @returns 
     */
    async _getScript(message: ChromeMessage) {
        const { id } = message.data
        const scripts = await ProjectService.list()
        const script = scripts.find(s => s.id === id)
        if (!script) {
            return ChromeWorker.getChromeResponse(false, undefined, 'script not found')
        }
        const coreCode = await this.userWorker.getRuntime(script);
        const code = await this.userWorker.getCode(script);
        return ChromeWorker.getChromeResponse(true, {
            info: script.window,
            title: script.name,
            code: `${coreCode};\n${code}`
        })
    }

    async _closeSidePanel() {
        await chrome.sidePanel.setOptions({ enabled: false })
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _installScript(message: ChromeMessage) {
        const url = message.data
        this._openSettings(`?installUrl=${url}`)
        return ChromeWorker.getChromeResponse(true, undefined)
    }

    async _loadPopupData() {
        const ar = await ProjectService.list();
        for (let i = 0; i < ar.length; i++) {
            const subscriptMap = this.userWorker.getScriptSubscriptMap(ar[i].id);
            if (typeof ar[i].runtime === 'undefined') {
                ar[i].runtime = {
                    subscriptMap,
                }
            } else {
                ar[i].runtime!.subscriptMap = subscriptMap;
            }

        }
        const validAr = ar.filter(a => {
            if (a.type !== 'content') {
                return true
            }
            return a.runtime && a.runtime?.subscriptMap.length > 0
        });
        return ChromeWorker.getChromeResponse(true, validAr)
    }

    async _dispatchMenuEvents(message: ChromeMessage) {
        const id = message.data;
        this.userWorker.dispatchMenuEvents(id);
        return ChromeWorker.getChromeResponse(true)
    }
}